/*
*  This file is part of mangusta
*
*  mangusta is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  mangusta is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with mangusta; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  US
*
*  Author: <antonino.calderone@ericsson.com>, <acaldmail@gmail.com>
*
*/


/* -------------------------------------------------------------------------- */

#include "nu_rt_prog_ctx.h"
#include "nu_stmt_decl.h"
#include "nu_error_codes.h"
#include "nu_expr_var.h"


/* -------------------------------------------------------------------------- */

namespace nu
{


// -------------------------------------------------------------------------

void stmt_decl_t::run(rt_prog_ctx_t & ctx)
{
   auto rt_error_if = [&](
                         bool cond,
                         rt_error_code_t::value_t err,
                         const std::string desc)
   {
      rt_error_code_t::get_instance().throw_if(
         cond,
         ctx.runtime_pc.get_line(),
         err,
         desc);
   };

   variant_t val = _arg->eval(ctx);
   auto vt = _vartype;

   // Resolve scope using the most nested one
   var_scope_t::handle_t scope =
      ctx.proc_scope.get(proc_scope_t::type_t::LOCAL);

   variant_t * var = nullptr;

   if (_reference)
   {
      rt_error_if(
         scope->is_defined(_variable),
         rt_error_code_t::E_VAR_REDEF,
         "'" + _variable + "'");

      auto var_ptr = dynamic_cast<expr_var_t*> (_arg.get());

      rt_error_if(
         !var_ptr,
         rt_error_code_t::E_TYPE_ILLEGAL,
         "'" + _variable + "'");

      if (!scope->is_defined(var_ptr->name()))
      {
         auto glb_scope =
            ctx.proc_scope.get(proc_scope_t::type_t::GLOBAL);

         rt_error_if(
            !glb_scope->is_defined(var_ptr->name()),
            rt_error_code_t::E_VAR_UNDEF,
            "'" + var_ptr->name() + "'");

         scope = glb_scope;
      }

      var = &((*scope)[var_ptr->name()]);

      rt_error_if(
         var->get_type() != vt,
         rt_error_code_t::E_TYPE_MISMATCH,
         "'" + _variable + "'");

      variant_t refvar(var);

      scope->define(_variable, refvar);

      ctx.go_to_next();

      return;
   }

   else
   {
      var = &(*scope)[_variable];
   }

   if (vt == variable_t::type_t::UNDEFINED)
      vt = var->get_type();

#if 0 //TODO
   bool is_vector = var->is_vector();


   size_t idx = 0;

   if (is_vector)
   {
      if (_vect_idx)
      {
         idx = _vect_idx->eval(ctx).to_int();

         //check size
         rt_error_if(
            idx >= var->vector_size(),
            rt_error_code_t::E_VEC_IDX_OUT_OF_RANGE,
            "'" + _variable + "(" + nu::to_string(idx) + ")'");
      }

      //check size
      rt_error_if(
         var->get_type() != val.get_type(),
         rt_error_code_t::E_TYPE_MISMATCH,
         "'" + _variable + "'");
   }

   else
   {
      rt_error_if(
         var->is_vector(),
         rt_error_code_t::E_TYPE_MISMATCH,
         "'" + _variable + "'");
   }

#endif

   rt_error_if(
      vt == variable_t::type_t::UNDEFINED,
      rt_error_code_t::E_VAR_UNDEF,
      "'" + _variable + "'");

   bool res = var->set(val) && var->cast_to(vt);

   rt_error_if(
      !res,
      rt_error_code_t::E_INVALID_ARGS,
      "'" + _variable + "'");

   var->set_const(_constant);

   ctx.go_to_next();
}


// -------------------------------------------------------------------------

}


/* -------------------------------------------------------------------------- */

